package com.espirit.eap;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

import javax.annotation.Resource;

import com.espirit.eap.manager.I18nTextProvider;
import com.googlecode.cswish.model.ModelInfo;
import com.googlecode.cswish.model.PropertyInfo;
import com.googlecode.cswish.struts.spring.BeanIntrospector;
import com.googlecode.cswish.util.OgnlExUtil;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.Validateable;
import com.opensymphony.xwork2.ValidationAware;
import com.opensymphony.xwork2.ValidationAwareSupport;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;

public class BaseAction extends HashMap<String, Object> implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable {

	private static final long serialVersionUID = -8467866495111595155L;

	protected static Logger logger = LoggerFactory.getLogger(BaseActionSupportAction.class);

    private final ValidationAwareSupport validationAware = new ValidationAwareSupport();

    private I18nTextProvider textProvider;
    
    private ModelInfo modelInfo;
    
    @Resource
	private OgnlExUtil ognlExUtil;
    
    public String getActionName() {
    	return this.getClass().getName();
    }
    
    public String getActionPath() {
    	return ActionContext.getContext().getActionInvocation().getProxy().getActionName();
    }
    
    @Override
    public Object put(String key, Object value) {
    	
    	if (modelInfo == null) {
			modelInfo =  BeanIntrospector.getInstance().getModelInfo(this.getClass().getName());
		}
    	PropertyInfo pi = modelInfo.getProperty(key.toString());
		if (pi != null) {
			if (value != null) {
				Class targetType = pi.getTypeClass();
				if (!targetType.isAssignableFrom(value.getClass())) {
		    		XWorkConverter converter = ognlExUtil.getXWorkConverter();
					value = converter.convertValue(value, targetType);
				}
			}
			pi.setValue(this, value, modelInfo);
		}
		Object oldValue = super.put(key, value);
    	return oldValue;
    }
    
    @Override
    public Object get(Object key) {
    	Object value = super.get(key);
    	
    	if (value == null && key != null) {
    		if (modelInfo == null) {
    			modelInfo =  BeanIntrospector.getInstance().getModelInfo(this.getClass().getName());
    		}
    		PropertyInfo pi = modelInfo.getProperty(key.toString());
    		if (pi != null) {
    			value = pi.getValue(this, modelInfo);
    		}
    	}
    	return value;
    }
    
    @Resource
    public void setI18nTextProvider(I18nTextProvider textProvider) {
    	this.textProvider = textProvider;
    	textProvider.setClazz(this.getClass());
    	textProvider.setLocaleProvider(this);
    }
	
	public void setActionErrors(Collection errorMessages) {
        validationAware.setActionErrors(errorMessages);
    }

    public Collection getActionErrors() {
        return validationAware.getActionErrors();
    }

    public void setActionMessages(Collection messages) {
        validationAware.setActionMessages(messages);
    }

    public Collection getActionMessages() {
        return validationAware.getActionMessages();
    }


    public void setFieldErrors(Map errorMap) {
        validationAware.setFieldErrors(errorMap);
    }

    public Map getFieldErrors() {
        return validationAware.getFieldErrors();
    }

    public Locale getLocale() {
        ActionContext ctx = ActionContext.getContext();
        if (ctx != null) {
            return ctx.getLocale();
        } else {
            logger.debug("Action context not initialized");
            return null;
        }
    }

    public String getText(String aTextName) {
        return getTextByLocale(aTextName, null);
    }

    public String getTextByLocale(String aTextName, String locale) {
		// Priority : locale --> current locale --> short key
		String text = textProvider.getTextByLocale(aTextName, locale, true);
		return text;
	}

    public String getText(String aTextName, String defaultValue) {
        return textProvider.getText(aTextName, defaultValue);
    }

    public String getText(String aTextName, String defaultValue, String obj) {
        return textProvider.getText(aTextName, defaultValue, obj);
    }

    public String getText(String aTextName, List args) {
        return textProvider.getText(aTextName, args);
    }

    public String getText(String key, String[] args) {
        return textProvider.getText(key, args);
    }

    public String getText(String aTextName, String defaultValue, List args) {
        return textProvider.getText(aTextName, defaultValue, args);
    }

    public String getText(String key, String defaultValue, String[] args) {
        return textProvider.getText(key, defaultValue, args);
    }

    public String getText(String key, String defaultValue, List args, ValueStack stack) {
        return textProvider.getText(key, defaultValue, args, stack);
    }

    public String getText(String key, String defaultValue, String[] args, ValueStack stack) {
        return textProvider.getText(key, defaultValue, args, stack);
    }

    public ResourceBundle getTexts() {
        return textProvider.getTexts();
    }

    public ResourceBundle getTexts(String aBundleName) {
        return textProvider.getTexts(aBundleName);
    }

    public void addActionError(String anErrorMessage) {
        validationAware.addActionError(anErrorMessage);
    }

    public void addActionMessage(String aMessage) {
        validationAware.addActionMessage(aMessage);
    }

    public void addFieldError(String fieldName, String errorMessage) {
        validationAware.addFieldError(fieldName, errorMessage);
    }

    public boolean hasActionErrors() {
        return validationAware.hasActionErrors();
    }

    public boolean hasActionMessages() {
        return validationAware.hasActionMessages();
    }

    public boolean hasErrors() {
        return validationAware.hasErrors();
    }

    public boolean hasFieldErrors() {
        return validationAware.hasFieldErrors();
    }

    /**
     * Clears field errors. Useful for Continuations and other situations
     * where you might want to clear parts of the state on the same action.
     */
    public void clearFieldErrors() {
        validationAware.clearFieldErrors();
    }

    /**
     * Clears action errors. Useful for Continuations and other situations
     * where you might want to clear parts of the state on the same action.
     */
    public void clearActionErrors() {
        validationAware.clearActionErrors();
    }

    /**
     * Clears messages. Useful for Continuations and other situations
     * where you might want to clear parts of the state on the same action.
     */
    public void clearMessages() {
        validationAware.clearMessages();
    }

    /**
     * Clears all errors. Useful for Continuations and other situations
     * where you might want to clear parts of the state on the same action.
     */
    public void clearErrors() {
        validationAware.clearErrors();
    }

    /**
     * Clears all errors and messages. Useful for Continuations and other situations
     * where you might want to clear parts of the state on the same action.
     */
    public void clearErrorsAndMessages() {
        validationAware.clearErrorsAndMessages();
    }

    /**
     * A default implementation that validates nothing.
     * Subclasses should override this method to provide validations.
     */
    public void validate() {
    }

    /**
     * <!-- START SNIPPET: pause-method -->
     * Stops the action invocation immediately (by throwing a PauseException) and causes the action invocation to return
     * the specified result, such as {@link #SUCCESS}, {@link #INPUT}, etc.
     * <p/>
     *
     * The next time this action is invoked (and using the same continuation ID), the method will resume immediately
     * after where this method was called, with the entire call stack in the execute method restored.
     * <p/>
     *
     * Note: this method can <b>only</b> be called within the {@link #execute()} method.
     * <!-- END SNIPPET: pause-method -->
     *
     * @param result the result to return - the same type of return value in the {@link #execute()} method.
     */
    public void pause(String result) {
    }

	@Override
	public boolean hasKey(String key) {
		return textProvider.hasKey(key);
	}

	@Override
	public String execute() {
		return null;
	}
}